#ifndef YEPXML_NODE_HPP
#define YEPXML_NODE_HPP

#include <string>
#include "type.hpp"
#include <map>
#include <sstream>

namespace yxml
{
    using namespace std;
    /**
     * @brief An XML Element
     *
     * This is intended as btree node. It also tracks reverse information (the upper and left nodes), but only for
     *  help on adding and removing operations. You can traverse the nodes using in the DeepFirst preorder fashion by using
     *  consecutives calling of next() method.
     *
     * The type() function identifies the types, that is one of Type values. Each type has a diferent behavor and way to
     *  interact:
     *   - \c TAG: Represents a entire xml tag (Both the openning and the closing). Only a tag can have subnodes (i.e only a tag
     *    can create children);
     *   - \c TEXT: A text block. It is all the content between an tag opening or closing and another one;
     *   - \c COMMENT: Everything between the <!-- and -->;
     *   - \c NONE: The null instance. It is a value direrent of any other and is used when it not founds a node.
     *   .
     *
     * The tag is the most flexible one. It can have subNodes and can be searched for. It fills the value as the tag name.
     *  (Example @<br /@> and @<div@> ... @</div@> tags have the value of "br" and "div", repectively).
     *
     * The entire text block and the characters between @<!-- and --@> are used
     **/
    class Node
    {
    public:
        ~Node(); ///< Removes and deletes a node;

        /**
         * @brief Gets the next traversal item.
         *
         * @param types The types or'd combination for filter.
         *
         * @return the next traversal or a @c NONE typed node if it as no one next.
         **/
        Node& next( int types = ALL );

        /**
         * @brief Gets the previous traversal item.
         *
         * @param types The types or'd combination for filter.
         *
         * @return the previous brother or a @c NONE typed node if it as no one before.
         **/
        Node& previous( int types = ALL );

        /**
         * @brief Gets the next brother item.
         *
         * @param types The types or'd combination for filter.
         *
         * @return the next brother or a @c NONE typed node if it as no one next.
         **/
        Node& nextBrother( int types = ALL );

        /**
         * @brief Gets the previous brother item.
         *
         * @param types The types or'd combination for filter.
         *
         * @return the next brother or a @c NONE typed node if it as no one before.
         **/
        Node& previousBrother( int types = ALL );

        /**
         * @brief Returns the node's Parent.
         *
         * @return The parent or a @c NONE typed node if it is the root node (also known as top or document node).
         **/
        Node& parent();

        /**
         * @brief Gets the first child.
         *
         * @param types The types or'd combination for filter.
         *
         * @return the first child or a @c NONE typed node if it is empty or if it is not a tag.
         **/
        Node& firstChild( int types = ALL );

        /**
         * @brief Gets the last child
         *
         * @param types The types or'd combination for filter.
         *
         * @return the last child or a @c NONE typed node if it is empty or if it is not a tag.
         **/
        Node& lastChild( int types = ALL );

        /**
         * @brief Adds a new node after this node.
         *
         * @param type the new node type;
         * @param value the new node's value.
         *
         * @return the brand new node.
         **/
        Node& addAfter( Type type, const string& value="" );

        /**
         * @brief Adds a new node before this node.
         *
         * @param type the new node type;
         * @param value the new node's value.
         *
         * @return the brand new node.
         **/
        Node& addBefore( Type type, const string& value="" );

        /**
         * @brief Adds a new node as child of this.
         *
         * @param type the new node type;
         * @param value the new node's value;
         * @param index the order number. It have to be less than or equal to count() method.
         *
         * @return the brand new node.
         **/
        Node& addChild( Type type, const string& value="", int index = -1 );

        /**
         * @brief Creates an string representation from the node.
         *
         * The representation is different according the type:
         *   - @c TEXT and @c COMMENT return their values;
         *   - @c TAG return all its @c TEXT children's values concatened;
         *   - @c NONE return an empty string.
         * @return An string representations for the node. It \b IS \b NOT an xml Representation
         **/
        string toString();

        /**
         * @brief Search for the i-th child tag sub-element.
         *
         * @param index The index to search.
         *
         * @return The tag found or a @c NONE typed element if it is not found.
         **/
        Node& operator[]( int index );

        /**
         * @brief Search for first child tag sub-element having the given value.
         *
         * @param value The value to search.
         *
         * @return The first tag found or a @c NONE typed element if it is not found.
         **/
        Node& operator[]( string value );

        /**
         * @brief Search for the i-th child tag sub-element having the given value;
         *
         * @param valuePosition A pair value position to be searched. The first element is the value
         *  and the second is the the ocurrence number.
         *
         * @return The tag found or a \c NONE typed element if it is not found.
         **/
        Node& operator[]( pair<string,int> valuePosition );

        /**
         * @brief Returns the number of subElements
         **/
        int count();

        /**
         * @brief Returns the number of subElements that have the value
         *
         * @param value The value to test;
         * @param types The types to filter. By default counts only Tags, to be compatible with
         *  operator[](string) and operator[](pair<string,int>) methods.
         **/
        int count( const string& value, int types = TAG );

        /**
         * @brief Returns the number of subElements of filtered subtype
         *
         * @param types The types to filter. By default counts only Tags, to be compatible with
         *  operator[](string) and operator[](pair<string,int>) methods.
         **/
        int count( int types );

        /**
         * @brief Returns the type
         **/
        Type type( void ) const
        {
            return _type;
        }

        /**
         * @brief Returns the value
         **/
        const string& value( void ) const
        {
            return _value;
        }

        /**
         * @brief Changes the value
         **/
        void value( const string& value )
        {
            _value = value;
        }

        /**
         * @brief Gets/Sets the attribute.
         **/
        string &attribute(const string &name)
        {
            return _attributes[name];
        }

        /**
         * @brief Get an attribute from the given type
         *
         * Use if, and only if, it is a non-string attribute
         **/
        template<typename T> T
        getAttribute(const string name)
        {
            T value;
            stringstream(_attributes[name]) >> value;
            return value;
        }

        /**
         * @brief Set an attribute from the given type
         *
         * Use if, and only if it is a non-string attribute
         **/
        template<typename T>
        void setAttribute(const string name, const T &value)
        {
            stringstream stream;
            stream << value;
            _attributes[name] = stream.str();
        }

        /**
         * @brief Get the attributes map.
         *
         * @return The internal map containing all attributes.
         *
         * @deprecated It breaks the abstraction of node class, giving direct access an to internal variable.
         *  Use attributesCount(), attribute(), attribute(string), attributeBegin() and attributeEnd() instead.
         **/
        map<string, string> &attributes()
        {
            return _attributes;
        }

        /**
         * @brief attributes count.
         **/
        size_t attributesCount() const;

        /**
         * @brief attribute Begin iterator.
         **/
        map<string, string>::const_iterator attributeBegin() const;

        /**
         * @brief attribute Begin iterator.
         **/
        map<string, string>::const_iterator attributeEnd() const;

        /**
         * @brief The null node
         *
         * That is node different from any other (with @c NONE type). It's used as default value instad \c NULL to avoid segmentation faults
         **/
        static Node& null();
    protected:
        Node( const Node& other ); ///< Copies all elements.
        /**
         * @brief Constructs the a node with the given type.
         *
         * This class can not be instancied directly, but only by the addAfter(), addBefore() and addChild() methods.
         *  It is necessary because the Document class is the only one that can be a root node. In that class you can
         *  add the childs.
         **/
        Node( Type type, const string& value );
    private:
        Node& operator =( const Node& other ); // To deny the attribution.

        static Node _null;

        Node* _firstChild; //left
        Node* _nextBrother; //right
        Node* _lastChild;
        Node* _previousBrother;
        Node* _parent;
        string _value;
        Type  _type;
        int _nChildren;
        map<string,string> _attributes;
    };
}

#endif // YEPXML_NODE_HPP
